﻿using Framework.Common;
using Framework.Task;
using System.Collections.Generic;

namespace Framework.Resource
{
    /// <summary>
    /// 资源管理类
    /// </summary>
    public class ResourceManager : IModule, IResourceManager
    {
        private static List<string> m_LoadingPackages = new List<string>();
        private static List<string> m_WaitLoadingPackages = new List<string>();

        /// <summary>
        /// 任务池
        /// </summary>
        private TaskPool<LoadAssetTask> m_TaskPool = null;
        /// <summary>
        /// 资源缓存
        /// </summary>
        private AssetCache m_AssetCache = null;
        /// <summary>
        /// 资源帮助类
        /// </summary>
        private IAssetHelper m_AssetHelper = null;
        /// <summary>
        /// 资源加载帮助类
        /// </summary>
        public IAssetHelper AssetHelper
        {
            get
            {
                return m_AssetHelper;
            }
        }

        #region 模块接口

        public int Priority
        {
            get
            {
                return ModulePriority.ResourcePriority;
            }
        }
        public void Init()
        {
            m_TaskPool = new TaskPool<LoadAssetTask>();
            m_AssetCache = new AssetCache();
        }
        public void Reset()
        {
            if(m_TaskPool != null)
                m_TaskPool.RemoveAllTask();
            m_AssetCache.ReleaseAllCache();
            m_LoadingPackages.Clear();
            m_WaitLoadingPackages.Clear();
        }
        public void Update(float frameTime, float realTime)
        {
            if (m_TaskPool != null)
                m_TaskPool.Update(frameTime, realTime);
        }
        public void Destroy()
        {
            if (m_TaskPool != null)
                m_TaskPool.Destroy();
            m_AssetCache.ReleaseAllCache();
            m_LoadingPackages.Clear();
            m_WaitLoadingPackages.Clear();
            //m_AssetHelper.Destroy();
        }

        #endregion

        #region 资源管理接口

        /// <summary>
        /// 获取所有资源包的数量
        /// </summary>
        public int AllAssetPackageCount
        {
            get
            {
                return m_AssetCache.PackageCount;
            }
        }
        /// <summary>
        /// 获得所有资源包的名称集合
        /// </summary>
        public List<string> AllAssetPackageNameList
        {
            get
            {
                return m_AssetCache.AllPackageName;
            }
        }
        
        #region 加载资源包

        /// <summary>
        /// 加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <returns>加载的资源包</returns>
        public AssetPackage LoadPackage(string packageName)
        {
            if (m_AssetHelper == null) return null;
            List<AssetPackage> dependPackages = LoadDepends(packageName);
            AssetPackage package = m_AssetCache.GetAssetPackage(packageName);
            if (package == null)
            {
                object obj = m_AssetHelper.LoadPackage(packageName);
                if (obj != null)
                {
                    package = new AssetPackage(packageName, obj, m_AssetHelper);
                    m_AssetCache.AddAssetPackage(packageName, package);
                }
            }
            if (dependPackages != null)
            {
                for (int i = 0; i < dependPackages.Count; i++)
                {
                    dependPackages[i].AddReferencePackage(package);
                    package.AddDependentPackage(dependPackages[i]);
                }
            }
            return package;
        }
        /// <summary>
        /// 加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="loadStartCallback">开始加载回调</param>
        /// <param name="loadSuccessCallback">加载成功回调</param>
        /// <param name="loadFailureCallBack">加载失败回调</param>
        /// <param name="loadUpdateCallback">加载更新回调</param>
        public void LoadPackageAsync(string packageName, FrameworkAction<string> loadStartCallback, FrameworkAction<string, AssetPackage> loadSuccessCallback, FrameworkAction<string, string> loadFailureCallBack, FrameworkAction<string, int> loadUpdateCallback)
        {
            LoadAssetAsyc(packageName, null, (pName, resName) => { if(loadStartCallback != null) loadStartCallback(pName); }, 
                          (pName, resName, package) => { if(loadSuccessCallback != null) loadSuccessCallback(resName, package as AssetPackage); }, 
                          (pName, resName, error) => { if(loadFailureCallBack != null) loadFailureCallBack(pName, error); }, 
                          (pName, resName, progress) => { if(loadUpdateCallback != null) loadUpdateCallback(pName, progress); });
        }
        /// <summary>
        /// 释放资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="isForce">是否强制释放</param>
        public void ReleasePackage(string packageName, bool isForce)
        {
            m_AssetCache.ReleasePackage(packageName, isForce);
        }

        #endregion

        #region 加载指定类型的资源

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <typeparam name="T">资源类型</typeparam>
        /// <param name="assetPackageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <returns>加载的资源</returns>
        public T LoadAsset<T>(string assetPackageName, string assetName)
        {
            AssetPackage package = LoadPackage(assetPackageName);
            T obj = package.LoadAsset<T>(assetName);
            if (obj != null)
                package.AddAssetReferenceCount(assetName);
            return obj;
        }
        /// <summary>
        /// 异步加载指定类型资源
        /// </summary>
        /// <param name="assetPackageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadStartCallback">加载资源开始回调</param>
        /// <param name="LoadSuccessCallback">加载资源成功回调</param>
        /// <param name="loadFailureCallBack">加载资源失败回调</param>
        /// <param name="loadUpdateCallback">加载资源更新回调</param>
        public void LoadAssetAsync<T>(string assetPackageName, string assetName, FrameworkAction<string, string> loadStartCallback, FrameworkAction<string, string, T> loadSuccessCallback, FrameworkAction<string, string, string> loadFailureCallBack, FrameworkAction<string, string, int> loadUpdateCallback)
        {
            LoadAssetAsyc(assetPackageName, null, (pName, resName) => { if (loadStartCallback != null) loadStartCallback(pName, resName); },
                          (pName, resName, package) => 
                          {
                              if (loadSuccessCallback != null)
                              {
                                  AssetPackage p = package as AssetPackage;
                                  T obj = p.LoadAsset<T>(assetName);
                                  if (obj != null)
                                      p.AddAssetReferenceCount(assetName);
                                  loadSuccessCallback(pName, assetName, obj);
                              }
                          },
                          (pName, resName, error) => { if (loadFailureCallBack != null) loadFailureCallBack(pName, resName, error); },
                          (pName, resName, progress) => { if (loadUpdateCallback != null) loadUpdateCallback(pName, resName, progress); });
        }
        /// <summary>
        /// 释放指定资源
        /// </summary>
        /// <param name="assetPackageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        public void ReleaseAsset(string packageName, string assetName)
        {
            AssetPackage package = m_AssetCache.GetAssetPackage(packageName);
            if (package == null) return;
            package.MinusAssetReferenceCount(assetName);
            //可以不去释放
            //ReleasePackage(packageName, false);
        }

        #endregion

        #region 创建游戏对象

        /// <summary>
        /// 创建游戏对象
        /// </summary>
        /// <param name="assetPackageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <returns>创建的游戏对象</returns>
        public object CreateGameObject(string assetPackageName, string assetName)
        {
            object asset = LoadAsset(assetPackageName, assetName);
            if (asset == null)
                return null;
            object instan = m_AssetHelper.InstantiateGameObject(asset);
            if (instan == null)
                return null;
            AddAssetInstance(assetPackageName, assetName, instan);
            return instan;
        }
        /// <summary>
        /// 异步创建游戏对象
        /// </summary>
        /// <param name="assetPackageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadStartCallback">创建游戏对象开始回调</param>
        /// <param name="LoadSuccessCallback">创建游戏对象成功回调</param>
        /// <param name="loadFailureCallBack">创建游戏对象失败回调</param>
        /// <param name="loadUpdateCallback">创建游戏对象更新回调</param>
        public void CreateGameObjectAsyc(string assetPackageName, string assetName, FrameworkAction<string, string> loadStartCallback, FrameworkAction<string, string, object> loadSuccessCallback, FrameworkAction<string, string, string> loadFailureCallBack, FrameworkAction<string, string, int> loadUpdateCallback)
        {
            LoadAssetAsyc(assetPackageName, assetName, loadStartCallback, 
                         (packageName, asName, asset) => 
                          {
                              object obj = m_AssetHelper.InstantiateGameObject(asset);
                              if (obj != null)
                                  AddAssetInstance(assetPackageName, assetName, obj);
                              if (loadSuccessCallback != null)
                                  loadSuccessCallback(packageName, asName, obj);
                          }, loadFailureCallBack, loadUpdateCallback);
        }
        /// <summary>
        /// 销毁一个游戏对象
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="gameObject">游戏对象</param>
        public void DestroyGameObject(string packageName, string assetName, object gameObject)
        {
            m_AssetHelper.DestroyGameObject(gameObject);
            DestroyAssetInstance(packageName, assetName, gameObject);
            //可以选择是否释放
            //ReleasePackage(packageName, false);
        }

        #endregion

        #region 释放资源

        /// <summary>
        /// 释放没有用到的资源
        /// </summary>
        public void ReleaseUnusedAssets()
        {
            if (m_AssetCache != null)
                m_AssetCache.ReleaseUnusedCache();
        }
        /// <summary>
        /// 释放所有的资源
        /// </summary>
        public void ReleaseAllAssets()
        {
            if (m_AssetCache != null)
                m_AssetCache.ReleaseAllCache();
        }

        #endregion

        /// <summary>
        /// 添加资源帮助类
        /// </summary>
        /// <param name="helper">资源帮助对象</param>
        public void AddAssetHelper(IAssetHelper helper)
        {
            m_AssetHelper = helper;
        }
        /// <summary>
        /// 添加资源加载代理器
        /// </summary>
        /// <param name="helper">资源加载接口</param>
        public void AddLoadAssetAgent(ILoadAssetHelper helper)
        {
            LoadAssetTaskAgent agent = new LoadAssetTaskAgent(this, helper);
            m_TaskPool.AddTaskAgent(agent);
        }

        #endregion

        #region 辅助方法

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <returns>加载的资源</returns>
        private object LoadAsset(string packageName, string assetName)
        {
            AssetPackage package = LoadPackage(packageName);
            return package.LoadAsset(assetName);
        }
        /// <summary>
        /// 加载资源依赖项
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        private List<AssetPackage> LoadDepends(string packageName)
        {
            string[] dependNames = m_AssetHelper.LoadDepends(packageName);
            if (dependNames == null || dependNames.Length <= 0)
                return null;
            List<AssetPackage> packages = new List<AssetPackage>();
            for (int i = 0; i < dependNames.Length; i++)
            {
                AssetPackage package = m_AssetCache.GetAssetPackage(dependNames[i]);
                if (package == null)
                {
                    object obj = m_AssetHelper.LoadPackage(dependNames[i]);
                    if (obj != null)
                    {
                        package = new AssetPackage(dependNames[i], obj, m_AssetHelper);
                        m_AssetCache.AddAssetPackage(dependNames[i], package);
                    }
                }
                packages.Add(package);
            }
            return packages;
        }
        /// <summary>
        /// 添加资源实例
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="instance">资源实例</param>
        private void AddAssetInstance(string packageName, string assetName, object instance)
        {
            AssetPackage package = m_AssetCache.GetAssetPackage(packageName);
            if (package == null) return;
            package.AddAssetInstance(assetName, instance);
        }
        /// <summary>
        /// 销毁一个资源实例
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="instance">资源实例对象</param>
        private void DestroyAssetInstance(string packageName, string assetName, object instance)
        {
            AssetPackage package = m_AssetCache.GetAssetPackage(packageName);
            if (package == null) return;
            package.RemoveAssetInstance(assetName, instance);
        }
        /// <summary>
        /// 加载资源
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="assetName">资源名称</param>
        /// <param name="loadStartCallback">加载资源成功回调</param>
        /// <param name="LoadSuccessCallback">加载资源成功回调</param>
        /// <param name="loadFailureCallBack">加载资源失败回调</param>
        /// <param name="loadUpdateCallback">加载资源更新回调</param>
        public void LoadAssetAsyc(string packageName, string assetName, FrameworkAction<string, string> loadStartCallback, FrameworkAction<string, string, object> loadSuccessCallback, FrameworkAction<string, string, string> loadFailureCallBack, FrameworkAction<string, string, int> loadUpdateCallback)
        {
            string[] dependentNames = m_AssetHelper.LoadDepends(packageName);
            LoadAssetTask task = new LoadAssetTask(packageName, assetName, dependentNames, loadStartCallback, loadSuccessCallback, loadFailureCallBack, loadUpdateCallback);
            m_TaskPool.AddTask(task);
            m_TaskPool.Update(0, 0);
        }

        #endregion

        /// <summary>
        /// 是否已经加载了资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public bool HasLoadAsset(string packageName)
        {
            return m_AssetCache.HasPackage(packageName);
        }
        /// <summary>
        /// 是否正在加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public bool IsLoadingPackage(string packageName)
        {
            return m_LoadingPackages.Contains(packageName);
        }
        /// <summary>
        /// 添加正在加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public void AddLoadingPackage(string packageName)
        {
            if (!m_LoadingPackages.Contains(packageName))
                m_LoadingPackages.Add(packageName);
        }
        /// <summary>
        /// 移除正在加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public void RemoveLoadingPackage(string packageName)
        {
            if (m_LoadingPackages.Contains(packageName))
                m_LoadingPackages.Remove(packageName);
        }
        /// <summary>
        /// 添加资源缓存
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        /// <param name="package">资源包</param>
        public AssetPackage AddAssetCache(string packageName, object package)
        {
            if (string.IsNullOrEmpty(packageName) || package == null) return null;
            AssetPackage assetPackage = new AssetPackage(packageName, package, m_AssetHelper);
            m_AssetCache.AddAssetPackage(packageName, assetPackage);
            return assetPackage;
        }
        /// <summary>
        /// 获得资源缓存
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public AssetPackage GetCache(string packageName)
        {
            return m_AssetCache.GetAssetPackage(packageName);
        }
        /// <summary>
        /// 添加等待加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public void AddWaitLoadingPackage(string packageName)
        {
            if(!m_WaitLoadingPackages.Contains(packageName))
                m_WaitLoadingPackages.Add(packageName);
        }
        /// <summary>
        /// 移除等待加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public void RemoveWaitLoadingPackage(string packageName)
        {
            if (m_LoadingPackages.Contains(packageName))
                m_WaitLoadingPackages.Remove(packageName);
        }
        /// <summary>
        /// 是否等待加载资源包
        /// </summary>
        /// <param name="packageName">资源包名称</param>
        public bool IsWaitLoadingPackage(string packageName)
        {
            return m_WaitLoadingPackages.Contains(packageName);
        }
    }
}